package de.danoeh.antennapod.ui.screen.drawer;

import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Build;
import android.view.ContextMenu;
import android.view.InputDevice;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.FitCenter;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedPreferences;
import de.danoeh.antennapod.storage.database.NavDrawerData;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.ui.common.ImagePlaceholder;
import de.danoeh.antennapod.ui.screen.InboxFragment;
import de.danoeh.antennapod.ui.screen.queue.QueueFragment;
import de.danoeh.antennapod.ui.screen.subscriptions.SubscriptionFragment;

import java.lang.ref.WeakReference;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * BaseAdapter for the navigation drawer
 */
public class NavListAdapter extends RecyclerView.Adapter<NavListAdapter.Holder>
        implements SharedPreferences.OnSharedPreferenceChangeListener {

    public static final int VIEW_TYPE_NAV = 0;
    public static final int VIEW_TYPE_SECTION_DIVIDER = 1;
    private static final int VIEW_TYPE_SUBSCRIPTION = 2;

    /**
     * a tag used as a placeholder to indicate if the subscription list should be displayed or not
     * This tag doesn't correspond to any specific activity.
     */
    public static final String SUBSCRIPTION_LIST_TAG = "SubscriptionList";

    private final List<String> fragmentTags = new ArrayList<>();
    private final ItemAccess itemAccess;
    private final WeakReference<Activity> activity;
    public boolean showSubscriptionList = true;

    public NavListAdapter(ItemAccess itemAccess, Activity context) {
        this.itemAccess = itemAccess;
        this.activity = new WeakReference<>(context);
        loadItems();
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        prefs.registerOnSharedPreferenceChangeListener(this);
    }

    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        if (UserPreferences.PREF_HIDDEN_DRAWER_ITEMS.equals(key)
                || UserPreferences.PREF_DRAWER_ITEM_ORDER.equals(key)) {
            loadItems();
        }
    }

    private void loadItems() {
        List<String> newTags = UserPreferences.getVisibleDrawerItemOrder();

        if (newTags.contains(SUBSCRIPTION_LIST_TAG)) {
            // we never want SUBSCRIPTION_LIST_TAG to be in 'tags'
            // since it doesn't actually correspond to a position in the list, but is
            // a placeholder that indicates if we should show the subscription list in the
            // nav drawer at all.
            showSubscriptionList = true;
            newTags.remove(SUBSCRIPTION_LIST_TAG);
        } else {
            showSubscriptionList = false;
        }

        fragmentTags.clear();
        fragmentTags.addAll(newTags);
        notifyDataSetChanged();
    }

    public List<String> getFragmentTags() {
        return Collections.unmodifiableList(fragmentTags);
    }

    @Override
    public int getItemCount() {
        int baseCount = getSubscriptionOffset();
        if (showSubscriptionList) {
            baseCount += itemAccess.getCount();
        }
        return baseCount;
    }

    @Override
    public long getItemId(int position) {
        int viewType = getItemViewType(position);
        if (viewType == VIEW_TYPE_SUBSCRIPTION) {
            return itemAccess.getItem(position - getSubscriptionOffset()).getId();
        } else if (viewType == VIEW_TYPE_NAV) {
            return -Math.abs((long) fragmentTags.get(position).hashCode()) - 1; // Folder IDs are >0
        } else {
            return 0;
        }
    }

    @Override
    public int getItemViewType(int position) {
        if (0 <= position && position < fragmentTags.size()) {
            return VIEW_TYPE_NAV;
        } else if (position < getSubscriptionOffset()) {
            return VIEW_TYPE_SECTION_DIVIDER;
        } else {
            return VIEW_TYPE_SUBSCRIPTION;
        }
    }

    public int getSubscriptionOffset() {
        return fragmentTags.size() > 0 ? fragmentTags.size() + 1 : 0;
    }

    @NonNull
    @Override
    public Holder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(activity.get());
        if (viewType == VIEW_TYPE_NAV) {
            return new NavHolder(inflater.inflate(R.layout.nav_listitem, parent, false));
        } else if (viewType == VIEW_TYPE_SECTION_DIVIDER) {
            return new DividerHolder(inflater.inflate(R.layout.nav_section_item, parent, false));
        } else {
            return new FeedHolder(inflater.inflate(R.layout.nav_listitem, parent, false));
        }
    }

    @Override
    public void onBindViewHolder(@NonNull Holder holder, int position) {
        int viewType = getItemViewType(position);

        holder.itemView.setOnCreateContextMenuListener(null);
        if (viewType == VIEW_TYPE_NAV) {
            bindNavView(NavigationNames.getLabel(fragmentTags.get(position)), position, (NavHolder) holder);
        } else if (viewType == VIEW_TYPE_SECTION_DIVIDER) {
            bindSectionDivider((DividerHolder) holder);
        } else {
            int itemPos = position - getSubscriptionOffset();
            DrawerItem item = itemAccess.getItem(itemPos);
            bindListItem(item, (FeedHolder) holder);
            if (item.isFeed()) {
                bindFeedView(item.asFeed(), (FeedHolder) holder);
            } else {
                bindTagView(item.asTag(), (FeedHolder) holder);
            }
            holder.itemView.setOnCreateContextMenuListener(itemAccess);
        }
        if (viewType != VIEW_TYPE_SECTION_DIVIDER) {
            holder.itemView.setSelected(itemAccess.isSelected(position));
            holder.itemView.setOnClickListener(v -> itemAccess.onItemClick(position));
            holder.itemView.setOnLongClickListener(v -> itemAccess.onItemLongClick(position));
            holder.itemView.setOnTouchListener((v, e) -> {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    if (e.isFromSource(InputDevice.SOURCE_MOUSE)
                            && e.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
                        itemAccess.onItemLongClick(position);
                        return false;
                    }
                }
                return false;
            });
        }
    }

    private void bindNavView(@StringRes int title, int position, NavHolder holder) {
        Activity context = activity.get();
        if (context == null) {
            return;
        }
        holder.title.setText(title);

        // reset for re-use
        holder.count.setVisibility(View.GONE);
        holder.count.setOnClickListener(null);
        holder.count.setClickable(false);

        String tag = fragmentTags.get(position);
        if (tag.equals(QueueFragment.TAG)) {
            int queueSize = itemAccess.getQueueSize();
            if (queueSize > 0) {
                holder.count.setText(NumberFormat.getInstance().format(queueSize));
                holder.count.setVisibility(View.VISIBLE);
            }
        } else if (tag.equals(InboxFragment.TAG)) {
            int unreadItems = itemAccess.getNumberOfNewItems();
            if (unreadItems > 0) {
                holder.count.setText(NumberFormat.getInstance().format(unreadItems));
                holder.count.setVisibility(View.VISIBLE);
            }
        } else if (tag.equals(SubscriptionFragment.TAG)) {
            int sum = itemAccess.getFeedCounterSum();
            if (sum > 0) {
                holder.count.setText(NumberFormat.getInstance().format(sum));
                holder.count.setVisibility(View.VISIBLE);
            }
        }

        holder.image.setImageResource(NavigationNames.getDrawable(fragmentTags.get(position)));
    }

    private void bindSectionDivider(DividerHolder holder) {
        Activity context = activity.get();
        if (context == null) {
            return;
        }

        if (UserPreferences.getSubscriptionsFilter().isEnabled() && showSubscriptionList) {
            holder.itemView.setEnabled(true);
            holder.feedsFilteredMsg.setVisibility(View.VISIBLE);
        } else {
            holder.itemView.setEnabled(false);
            holder.feedsFilteredMsg.setVisibility(View.GONE);
        }
    }

    private void bindListItem(DrawerItem item, FeedHolder holder) {
        if (item.getCounter() > 0) {
            holder.count.setVisibility(View.VISIBLE);
            holder.count.setText(NumberFormat.getInstance().format(item.getCounter()));
        } else {
            holder.count.setVisibility(View.GONE);
        }
        holder.title.setText(item.getTitle());
        int padding = (int) (activity.get().getResources().getDimension(R.dimen.thumbnail_length_navlist) / 2);
        holder.itemView.setPadding(item.getLayer() * padding, 0, 0, 0);
    }

    private void bindFeedView(Feed feed, FeedHolder holder) {
        Activity context = activity.get();
        if (context == null) {
            return;
        }

        float radius = 4 * context.getResources().getDisplayMetrics().density;
        Glide.with(context)
                .load(feed.getImageUrl())
                .apply(new RequestOptions()
                    .placeholder(ImagePlaceholder.getDrawable(context, radius))
                    .error(ImagePlaceholder.getDrawable(context, radius))
                    .transform(new FitCenter(),
                            new RoundedCorners((int) radius))
                    .dontAnimate())
                .into(holder.image);

        holder.failure.setVisibility(feed.hasLastUpdateFailed() ? View.VISIBLE : View.GONE);
    }

    private void bindTagView(NavDrawerData.TagItem tag, FeedHolder holder) {
        Activity context = activity.get();
        if (context == null) {
            return;
        }
        if (tag.isOpen()) {
            holder.count.setVisibility(View.GONE);
        }
        if (FeedPreferences.TAG_UNTAGGED.equals(tag.getTitle())) {
            holder.title.setText(R.string.tag_untagged);
        }
        Glide.with(context).clear(holder.image);
        holder.image.setImageResource(R.drawable.ic_tag);
        holder.failure.setVisibility(View.GONE);
    }

    static class Holder extends RecyclerView.ViewHolder {
        public Holder(@NonNull View itemView) {
            super(itemView);
        }
    }

    static class DividerHolder extends Holder {
        final LinearLayout feedsFilteredMsg;

        public DividerHolder(@NonNull View itemView) {
            super(itemView);
            feedsFilteredMsg = itemView.findViewById(R.id.nav_feeds_filtered_message);
        }
    }

    static class NavHolder extends Holder {
        final ImageView image;
        final TextView title;
        final TextView count;

        public NavHolder(@NonNull View itemView) {
            super(itemView);
            image = itemView.findViewById(R.id.imgvCover);
            title = itemView.findViewById(R.id.txtvTitle);
            count = itemView.findViewById(R.id.txtvCount);
        }
    }

    static class FeedHolder extends Holder {
        final ImageView image;
        final TextView title;
        final ImageView failure;
        final TextView count;

        public FeedHolder(@NonNull View itemView) {
            super(itemView);
            image = itemView.findViewById(R.id.imgvCover);
            title = itemView.findViewById(R.id.txtvTitle);
            failure = itemView.findViewById(R.id.itxtvFailure);
            count = itemView.findViewById(R.id.txtvCount);
        }
    }

    public interface ItemAccess extends View.OnCreateContextMenuListener {
        int getCount();

        DrawerItem getItem(int position);

        boolean isSelected(int position);

        int getQueueSize();

        int getNumberOfNewItems();

        int getNumberOfDownloadedItems();

        int getReclaimableItems();

        int getFeedCounterSum();

        void onItemClick(int position);

        boolean onItemLongClick(int position);

        @Override
        void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo);
    }

}
